﻿using ArtMath.Attribute;
using ArtMath.Component.Point;
using ArtMath.Core.Data;
using ArtMath.Data;
using ArtMath.Helper;
using ArtMath.PointCollection;
using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using ArtMath.Resource;
using ArtMath.Flag;
using ArtMath.CommonType;

namespace ArtMath.Component.Circle
{
    /// <summary>
    /// 弦高弓形
    /// </summary>
    [Serializable]
    public class ChordHeightArch : PartialCircleBase
    {
        PadStringX height;
        public ChordHeightArch(SketchPad pad, string name, string height, bool addToList = true) : base(pad, name, ToolTypeName.xggx, addToList)
        {
            pske.Curves = new Curve[] { new Curve() };
            InPoints = new PointBase[2];
            Height = new PadStringX(Pad, height);
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                PointCollectionsManager res = base.Skeleton;
                LineSegment ls = new LineSegment(Point2.ToPointD(), Point1.ToPointD());
                res.AddPointCollection(ls);
                res.ConnectHeadAndTail(ls, ske);
                res.ConnectHeadAndTail(ske, ls);
                return res;
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                PointCollectionsManager res = base.PSkeleton;
                LineSegment ls = new LineSegment(Point2.Pxy, Point1.Pxy);
                res.AddPointCollection(ls);
                res.ConnectHeadAndTail(ls, pske);
                res.ConnectHeadAndTail(pske, ls);
                return res;
            }
        }
        public override PointCollectionsManager NewSkeleton
        {
            get
            {
                PointCollectionsManager res = base.NewSkeleton;
                PointCollectionBase tmp = res.PointCollections.Single();
                LineSegment ls = new LineSegment(Point2.NewXY, Point1.NewXY);
                res.AddPointCollection(ls);
                res.ConnectHeadAndTail(ls, tmp);
                res.ConnectHeadAndTail(tmp, ls);
                return res;
            }
        }
        public override PointCollectionsManager NewPSkeleton
        {
            get
            {
                PointCollectionsManager res = base.NewPSkeleton;
                PointCollectionBase tmp = res.PointCollections.Single();
                LineSegment ls = new LineSegment(Point2.NewPxy, Point1.NewPxy);
                res.AddPointCollection(ls);
                res.ConnectHeadAndTail(ls, tmp);
                res.ConnectHeadAndTail(tmp, ls);
                return res;
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("Height")]
        public PadStringX Height
        {
            get { return height; }
            set
            {
                if (height is object)
                {
                    height.ExpressionSetting -= Height_ExpressionSetting;
                    height.ExpressionSet -= Height_ExpressionSet;
                    height.ParametersValueChanging -= Height_ParametersValueChanging;
                    height.ParametersValueChanged -= Height_ParametersValueChanged;
                }
                height = value;
                if (height is object)
                {
                    height.ExpressionSetting += Height_ExpressionSetting;
                    height.ExpressionSet += Height_ExpressionSet;
                    height.ParametersValueChanging += Height_ParametersValueChanging;
                    height.ParametersValueChanged += Height_ParametersValueChanged;
                }
            }
        }

        void Height_ParametersValueChanging(DocStringX sender)
        {
            Point1.TempMoveLock = Point2.TempMoveLock = true;
            Pad.MoveObject_DoMouseDown(this);
            Point1.TempMoveLock = Point2.TempMoveLock = false;
        }

        void Height_ExpressionSetting(StringX sender)
        {
            Point1.TempMoveLock = Point2.TempMoveLock = true;
            Pad.MoveObject_DoMouseDown(this);
            Point1.TempMoveLock = Point2.TempMoveLock = false;
        }

        void Height_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            Point1.TempMoveLock = Point2.TempMoveLock = true;
            if (Pad.MoveObject_DoMouseMove(this, PointD.Empty))
            {
                ComponentChanged(this);
            }
            else
            {
                Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("Height")), MessageType.Error);
                Height.SetExpression(e.OldExpression, false);
            }
            Pad.MoveObject_DoMouseUp(this);
            Point1.TempMoveLock = Point2.TempMoveLock = false;
        }
        void Height_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            Point1.TempMoveLock = Point2.TempMoveLock = true;
            if (Pad.MoveObject_DoMouseMove(this, PointD.Empty))
            {
                ComponentChanged(this);
            }
            else
            {

            }
            Pad.MoveObject_DoMouseUp(this);
            Point1.TempMoveLock = Point2.TempMoveLock = false;
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}+{1}1", "Chord", "EndPoint")]
        public PointBase Point1
        {
            get { return InPoints[0]; }
            set { SetPoint(0, value); }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}+{1}2", "Chord", "EndPoint")]
        public PointBase Point2
        {
            get { return InPoints[1]; }
            set { SetPoint(1, value); }
        }
        [MultilingualDisplayName("ArchCenter")]
        public override PointD Center
        {
            get
            {
                return base.Center;
            }
        }
        [MultilingualDisplayName(null, "ArchCenter", "Displayer")]
        public override PropertyDisplayer CenterDisplayer
        {
            get
            {
                return base.CenterDisplayer;
            }

            set
            {
                base.CenterDisplayer = value;
            }
        }
        public override bool CanDrawVirtual
        {
            get
            {
                return Point1 != null;
            }
        }

        public override double NewStart
        {
            get { return NewCenter.GetAngle(Point1.NewXY); }
        }

        public override double NewPStart
        {
            get { return NewPCenter.GetAngle(Point1.NewPxy); }
        }

        public override double NewChordLength
        {
            get { return Point1.NewXY.DistanceTo(Point2.NewXY); }
        }
        public override double NewRadius
        {
            get
            {
                double d = NewChordLength / 2, h = Height.Value;
                return (d * d + h * h) / (2 * Math.Abs(h));
            }
        }
        public override PointD NewCenter
        {
            get
            {
                PointD mid = (Point1.NewXY + Point2.NewXY) / 2;
                double d = NewChordLength / 2, h = Height.Value, x = Math.Abs((d * d - h * h) / (2 * h)), absh = Math.Abs(h);
                return mid.Offset(Point1.NewXY.GetAngle(Point2.NewXY) + Core.Helper.Angle.RightAngle * Math.Sign(h) * (absh <= d ? -1 : 1), x);
            }
        }
        public override PointD NewPCenter
        {
            get { return Pad.XYToPxy(NewCenter); }
        }
        public override double NewSweep
        {
            get
            {
                double d = NewChordLength / 2, h = Height.Value,
                    absh = Math.Abs(h), res = Utility.GetAngle(NewCenter, Point1.NewXY, Point2.NewXY);
                if (absh > d)
                {
                    res = Core.Helper.Angle.RadRound - res;
                }
                return res * -Math.Sign(h);
            }
        }
        public override double NewPSweep
        {
            get
            {
                double d = NewChordLength / 2, h = Height.Value,
                    absh = Math.Abs(h), res = Utility.GetAngle(NewPCenter, Point1.NewPxy, Point2.NewPxy);
                if (absh > d)
                {
                    res = Core.Helper.Angle.RadRound - res;
                }
                return res * Math.Sign(h);
            }
        }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible && IsCreationCompleted)
            {
                Brush bru = FillBrush;
                if (bru != null) pske.Curves[0].Paint(gra, null, bru, Curve.FillType.Path);
                gra.DrawArc(LinePen, PCenter, Pad.Coordinate == null ? 0 : -Pad.Coordinate.RotateAngle.DegreeValue, pske.LongAxisHalfLength, pske.ShortAxisHalfLength, PStart * Core.Helper.Angle.RToD, PSweep * Core.Helper.Angle.RToD);
                gra.DrawLineSegment(LinePen, Point2.Pxy, Point1.Pxy);
            }
            base.Paint(gra);
        }
        public override void PaintTrack(Graphics gra)
        {
            if (ShowTrack_)
            {
                for (int i = 0; i < track.Count; i++)
                {
                    gra.DrawSector(TrackPen, null, track[i].Key, track[i].Value[0], track[i].Value[1], track[i].Value[2], track[i].Value[3], track[i].Value[4]);
                }
            }
            base.PaintTrack(gra);
        }
        public override void PaintVirtualObjects(Graphics gra)
        {
            if (CanDrawVirtual)
            {
                pske.Curves[0].Paint(gra, null, Core.Helper.Painter.VirtualBrush, Curve.FillType.Path);
                gra.DrawArc(PCenter, Pad.Coordinate == null ? 0 : -Pad.Coordinate.RotateAngle.DegreeValue, pske.LongAxisHalfLength, pske.ShortAxisHalfLength, PStart * Core.Helper.Angle.RToD, PSweep * Core.Helper.Angle.RToD);
                gra.DrawLineSegment(SpareCoordinate_, Point1.Pxy);
            }
            base.PaintVirtualObjects(gra);
        }
        public override void ComponentBuilding(Base component)
        {
            if (Point1 == null) return;
            if (Flags.Shift)
            {
                SpareCoordinate = SpareCoordinate.GetProjectivePoint(Point1.Pxy, Math.Tan(Utility.GetFixedAngle(
                        Point1.Pxy.GetAngle(SpareCoordinate))));
            }
            PartialCircle pc = ske as PartialCircle;
            PartialEllipse pe = pske as PartialEllipse;
            PointD xy = Pad.PxyToXY(SpareCoordinate), mid = (Point1.ToPointD() + xy) / 2;
            ChordLength = xy.DistanceTo(Point1.ToPointD());
            double d = ChordLength / 2, h = Height.Value,
                x = Math.Abs((d * d - h * h) / (2 * h)), absh = Math.Abs(h);
            Radius = (d * d + h * h) / (2 * absh);
            ske.Center = mid.Offset(Point1.ToPointD().GetAngle(xy) + Core.Helper.Angle.RightAngle * Math.Sign(h) * (absh <= d ? -1 : 1), x);
            pske.Center = Pad.XYToPxy(ske.Center);
            pc.Start = Center.GetAngle(Point1.ToPointD());
            pe.Start = PCenter.GetAngle(Point1.Pxy);
            pc.Sweep = Utility.GetAngle(Center, Point1.ToPointD(), xy);
            pe.Sweep = Utility.GetAngle(PCenter, Point1.Pxy, SpareCoordinate);
            if (absh > d)
            {
                pc.Sweep = Core.Helper.Angle.RadRound - pc.Sweep;
                pe.Sweep = Core.Helper.Angle.RadRound - pe.Sweep;
            }
            pc.Sweep *= -Math.Sign(h);
            pe.Sweep *= Math.Sign(h);
            ArcLength = Radius * pc.Sweep;
            Circumference = ArcLength + ChordLength;
            Area = Math.PI * Radius * Radius * CentralAngle / Core.Helper.Angle.RadRound + 0.5 * ChordLength * x * (absh <= d ? -1 : 1);
            Pad.BuildArcSamples(ske.Curves[0], pske.Curves[0], Center, Radius, Start, Sweep);
            ShowStatus($"{Resources.Translate("Creating")}{Resources.ColonSeparate}{Name}{Resources.LeftParenthesis}{Resources.Translate("ArcLength")} = {ArcLength}{Resources.CommaSeparate}{Resources.Translate("ChordLength")} = {ChordLength}{Resources.CommaSeparate}{Resources.Translate("ArchCenter")}{Resources.ColonSeparate}{Pad.ShowPoint(Center)}{Resources.CommaSeparate}{Resources.Translate("Radius")} = {Radius}{Resources.CommaSeparate}{Resources.Translate("CentralAngle")} = {CentralAngle}");
            base.ComponentBuilding(component);
        }
        public override void ComponentChanged(ShapeBase component)
        {
            base.ComponentChanged(component);
            Circumference = ArcLength + ChordLength;
            double d = ChordLength / 2, h = Height.Value,
                x = Math.Abs((d * d - h * h) / (2 * h)), absh = Math.Abs(h);
            Area = Math.PI * Radius * Radius * CentralAngle / Core.Helper.Angle.RadRound + 0.5 * ChordLength * x * (absh <= d ? -1 : 1);
        }
        public override void CreateComponents()
        {
            if (Point1 == null)
            {
                Point1 = Pad.CreatePoint($"{Resources.Translate("Chord")}{Resources.Join}{Resources.Translate("EndPoint")}1", SpareCoordinate_);
                Pad.TrackObj = this;
            }
            else
            {
                Point2 = Pad.CreatePoint($"{Resources.Translate("Chord")}{Resources.Join}{Resources.Translate("EndPoint")}2", SpareCoordinate_, true, new PointCollectionBase[] { new Universal(Point1.ToPointD()) });
                base.CreateComponents();
            }
        }
    }
}
